package meatslope2.gameengine.components;

import meatslope2.application.SystemRegistry;
import meatslope2.gameengine.engine.CollisionSystem;
import meatslope2.gameengine.gameobjects.GameObject;
import meatslope2.gameengine.primatives.HitInfo;
import meatslope2.gameengine.primatives.Vector2;

//TODO: make sure within world
public class CollisionComponent extends Component
{
	//Caches the beginning of each ray
	private final Vector2 previousCenter = new Vector2();
	
	//Caches prevMovement - movement
	private final Vector2 deltaMovement = new Vector2();
	
	//Offset is used to compute the end point of the array for testPointEnd
	private final Vector2 testPointOffset = new Vector2();
	private final Vector2 testPointEnd = new Vector2();
	
	private final HitInfo verticalHit = new HitInfo();
	private final HitInfo horizontalHit = new HitInfo();
	
	public HitInfo getVerticalHit()
	{
		return this.verticalHit;
	}
	
	public HitInfo getHorizontalHit()
	{
		return this.horizontalHit;
	}
	
	//Offsets used to add to player position in order to get the correct ray ending
	private final int offsetX;
	private final int offsetY;
	
	public CollisionComponent(GameObject gameObject)
	{
		super(gameObject);
		
		this.offsetX = gameObject.getWidth() / 2;
		this.offsetY = gameObject.getHeight() / 2;
	}
	
	@Override
	public void update()
	{
		//Be wary!  if you set these, you will affect the player!
		final Vector2 prevPlayerPosition = this.gameObject.getPrevPosition();
		final Vector2 playerPosition = this.gameObject.getPosition();
		
		//Don't run this on the first update cycle
		if(prevPlayerPosition.length2() == 0)
			return;
        
        deltaMovement.set(playerPosition);
        deltaMovement.subtract(prevPlayerPosition);
        
        previousCenter.set(this.offsetX, this.offsetY);
        previousCenter.add(prevPlayerPosition);
        
        //Reset our hitInfos before continuing
        verticalHit.reset();
        horizontalHit.reset();
        
        if (Math.abs(this.deltaMovement.x) > Math.abs(this.deltaMovement.y))
        {
        	sweepHorizontal(playerPosition);
        	sweepVertical(playerPosition);
        }
        else
        {
        	sweepVertical(playerPosition);
        	sweepHorizontal(playerPosition);
        }
	}
		
	/* Sweeps the space between two points looking for surfaces that oppose horizontal movement. */
    protected void sweepHorizontal(Vector2 playerPosition) 
    {
    	if(this.deltaMovement.x == 0)
    		return;
    	
    	final CollisionSystem collisionSystem = SystemRegistry.collisionSystem;
    	
        // Shoot a ray from the center of the previous frame's box to the edge (left or right,
        // depending on the direction of movement) of the current box.
        this.testPointOffset.y = this.offsetY;
        this.testPointOffset.x = 0;
        if (this.deltaMovement.x > 0.0f)
        	this.testPointOffset.x = this.gameObject.getWidth();
        
        this.testPointEnd.set(playerPosition);
        this.testPointEnd.add(testPointOffset);
        
        HitInfo newHorizontalHit = collisionSystem.castRay(previousCenter, testPointEnd);
        this.horizontalHit.set(newHorizontalHit);
        
        if(verticalHit.isHit())
        	playerPosition.set(playerPosition.x, verticalHit.getHitPoint().y);
    }
    
    /* Sweeps the space between two points looking for surfaces that oppose vertical movement. */
    protected void sweepVertical(Vector2 playerPosition) 
    {
    	if(this.deltaMovement.y == 0)
    		return;
    	
    	final CollisionSystem collisionSystem = SystemRegistry.collisionSystem;
    	
        // Shoot a ray from the center of the previous frame's box to the edge (top or bottom,
        // depending on the direction of movement) of the current box.
        this.testPointOffset.x = this.offsetX;
        this.testPointOffset.y = 0;
        if (deltaMovement.y > 0.0f) 
        	this.testPointOffset.y = this.gameObject.getHeight();
        
        this.testPointEnd.set(playerPosition);
        this.testPointEnd.add(testPointOffset);
        
        HitInfo newVerticalHit = collisionSystem.castRay(previousCenter, testPointEnd);
        this.verticalHit.set(newVerticalHit);
        
        if(verticalHit.isHit())
        	playerPosition.set(playerPosition.x, verticalHit.getHitPoint().y);
    }
}
